﻿using System;
using System.Collections.Generic;
using System.Text;
using btom = Microsoft.BizTalk.ExplorerOM;

namespace HenIT.BizTalk.Objects
{
    public class BizTalkGroup
    {
        public string Name { get; set; }
        public string ConnectionString { get; set; }
        public string LoadingErrors { get; set; }
        public string ComparingErrors { get; set; }

        private List<Application> applications = new List<Application>();
        public List<Application> Applications
        {
            get { return applications; }
        }

        #region Load data
        public void LoadGroupInfo(btom.BtsCatalogExplorer catalog, string appFilter)
        {
            catalog.Refresh();
            ConnectionString = catalog.ConnectionString;
            Name = catalog.GroupName;
            LoadingErrors = "";

            foreach (btom.Application btsApplication in catalog.Applications)
            {
                if (!btsApplication.IsSystem && (appFilter.Length == 0 || btsApplication.Name.StartsWith(appFilter)))
                {
                    applications.Add(LoadApplication(btsApplication));
                }
            }
        }

        private Application LoadApplication(btom.Application btsApplication)
        {
            Application application = new Application();
            try
            {                
                application.Name = btsApplication.Name;

                #region References
                foreach (btom.Application refApp in btsApplication.References)
                {
                    application.References.Add(new StringMatch(refApp.Name));
                } 
                #endregion

                #region Assemblies
                foreach (btom.BtsAssembly btsAssembly in btsApplication.Assemblies)
                {
                    HitAssembly hitAssembly = new HitAssembly();
                    hitAssembly.Name = btsAssembly.Name;
                    hitAssembly.DisplayName = btsAssembly.DisplayName;
                    hitAssembly.Version = new StringMatch(btsAssembly.Version);
                    hitAssembly.Culture = new StringMatch(btsAssembly.Culture);

                    foreach (btom.Schema btsSchema in btsAssembly.Schemas)
                    {
                        Schema schema = new Schema();
                        schema.Name = btsSchema.FullName;
                        schema.MessageType = "";
                        if (btsSchema.TargetNameSpace != null)
                        {
                            schema.MessageType = btsSchema.TargetNameSpace;
                            schema.TargetNamespace = btsSchema.TargetNameSpace;
                        }
                        if (btsSchema.RootName != null)
                        {
                            schema.Root = btsSchema.RootName;
                            if (schema.MessageType.Length > 0)
                                schema.MessageType += "#";
                            schema.MessageType += btsSchema.RootName;
                        }                       
                        
                        hitAssembly.Schemas.Add(schema);
                    }
                    foreach (btom.Transform btsMap in btsAssembly.Transforms)
                    {
                        hitAssembly.Maps.Add(new StringMatch(btsMap.FullName));
                    }
                    foreach (btom.Pipeline btsPipeLine in btsAssembly.Pipelines)
                    {
                        hitAssembly.Pipelines.Add(new StringMatch(btsPipeLine.FullName));
                    }
                    foreach (btom.BtsOrchestration btsOrchestration in btsAssembly.Orchestrations)
                    {
                        Orchestration orc = new Orchestration();
                        orc.Name = btsOrchestration.FullName;
                        orc.Host = new StringMatch(btsOrchestration.Host == null ? "N/A" : btsOrchestration.Host.Name);
                        foreach (btom.OrchestrationPort port in btsOrchestration.Ports)
                        {
                            orc.Ports.Add(new StringMatch(port.Name));
                        }
                        hitAssembly.Orchestrations.Add(orc);
                    }
                    application.Assemblies.Add(hitAssembly);
                } 
                #endregion

                #region Ports
                #region Receive ports
                foreach (btom.ReceivePort rp in btsApplication.ReceivePorts)
                {
                    ReceivePort receivePort = new ReceivePort();
                    receivePort.Name = rp.Name;
                    receivePort.RouteFailedMessage = new BoolMatch(rp.RouteFailedMessage);
                    receivePort.IsTwoWay = new BoolMatch(rp.IsTwoWay);
                    receivePort.AuthenticationType = new StringMatch(rp.Authentication.ToString());
                    foreach (btom.Transform map in rp.InboundTransforms)
                    {
                        receivePort.InboundMaps.Add(new StringMatch(map.FullName));
                    }
                    if (receivePort.IsTwoWay.Value)
                        foreach (btom.Transform map in rp.OutboundTransforms)
                        {
                            receivePort.OutboundMaps.Add(new StringMatch(map.FullName));
                        }
                    #region ReceiveLocations
                    foreach (btom.ReceiveLocation rl in rp.ReceiveLocations)
                    {
                        receivePort.Receivelocations.Add(new ReceiveLocation()
                            {
                                Name = rl.Name,
                                IsPrimary = new BoolMatch(rl.IsPrimary),
                                TransportType = new StringMatch(rl.TransportType.Name),
                                TransportTypeData = new StringMatch(rl.TransportTypeData),
                                ReceivePipeline = new StringMatch(ReadReceivePipelineName(rl.ReceivePipeline)),
                                SendPipeline = new StringMatch(ReadReceivePipelineName(rl.SendPipeline)),
                                ReceiveHandlerHost = new StringMatch(rl.ReceiveHandler.Host.Name),
                                ReceivePipelineData = new StringMatch(N(rl.ReceivePipelineData)),
                                SendPipelineData = new StringMatch(N(rl.SendPipelineData)),
                                ServiceWindowEnabled = new BoolMatch(rl.ServiceWindowEnabled),
                                FromTime = new DateTimeMatch(rl.FromTime),
                                ToTime = new DateTimeMatch(rl.ToTime),
                                StartDateEnabled = new BoolMatch(rl.StartDateEnabled),
                                StartDate = new DateTimeMatch(rl.StartDate),
                                EndDateEnabled = new BoolMatch(rl.EndDateEnabled),
                                EndDate = new DateTimeMatch(rl.EndDate)
                            }
                           );
                    } 
                    #endregion
                    application.ReceivePorts.Add(receivePort);
                } 
                #endregion

                #region Send ports
                foreach (btom.SendPort sp in btsApplication.SendPorts)
                {
                    SendPort sendPort = new SendPort();
                    sendPort.Name = sp.Name;
                    sendPort.IsTwoWay = new BoolMatch(sp.IsTwoWay);
                    sendPort.IsDynamic = new BoolMatch(sp.IsDynamic);
                    sendPort.Filter = new StringMatch(sp.Filter);
                    sendPort.RouteFailedMessage = new BoolMatch(sp.RouteFailedMessage);
                    sendPort.OrderedDelivery = new BoolMatch(sp.OrderedDelivery);
                    sendPort.Priority = new IntMatch(sp.Priority);
                    sendPort.StopSendingOnFailure = new BoolMatch(sp.StopSendingOnFailure);
                    sendPort.ReceivePipeline = new StringMatch(ReadReceivePipelineName(sp.ReceivePipeline));
                    sendPort.ReceivePipelineData = new StringMatch(N(sp.ReceivePipelineData));
                    sendPort.SendPipeline = new StringMatch(ReadReceivePipelineName(sp.SendPipeline));
                    sendPort.SendPipelineData = new StringMatch(N(sp.SendPipelineData));
                    if (sp.IsTwoWay)
                        foreach (btom.Transform map in sp.InboundTransforms)
                        {
                            sendPort.InboundMaps.Add(new StringMatch(map.FullName));
                        }
                    if (sp.OutboundTransforms != null)
                        foreach (btom.Transform map in sp.OutboundTransforms)
                        {
                            sendPort.OutboundMaps.Add(new StringMatch(map.FullName));
                        }
                    if (sp.IsDynamic)
                    {
                        sendPort.PrimaryTransport.IsUsed.Value = false;
                        sendPort.SecondaryTransport.IsUsed.Value = false;
                    }
                    else
                    {
                        sendPort.PrimaryTransport.IsUsed.Value = true;
                        sendPort.PrimaryTransport.RetryCount = new IntMatch(sp.PrimaryTransport.RetryCount);
                        sendPort.PrimaryTransport.RetryInterval = new IntMatch(sp.PrimaryTransport.RetryInterval);
                        sendPort.PrimaryTransport.ServiceWindowEnabled = new BoolMatch(sp.PrimaryTransport.ServiceWindowEnabled);
                        sendPort.PrimaryTransport.FromTime = new DateTimeMatch(sp.PrimaryTransport.FromTime);
                        sendPort.PrimaryTransport.ToTime = new DateTimeMatch(sp.PrimaryTransport.ToTime);

                        if (sp.PrimaryTransport.SendHandler != null)
                        {
                            sendPort.PrimaryTransport.SendHandler = new StringMatch(sp.PrimaryTransport.SendHandler.Name);
                            sendPort.PrimaryTransport.SendHandlerHost = new StringMatch(sp.PrimaryTransport.SendHandler.Host.Name);
                        }
                        if (sp.PrimaryTransport.TransportType != null)
                        {
                            sendPort.PrimaryTransport.TransportType = new StringMatch(sp.PrimaryTransport.TransportType.Name);
                        }

                        if (sp.SecondaryTransport != null && sp.SecondaryTransport.TransportType != null)
                        {
                            sendPort.SecondaryTransport.IsUsed.Value = true;
                            sendPort.SecondaryTransport.RetryCount = new IntMatch(sp.SecondaryTransport.RetryCount);
                            sendPort.SecondaryTransport.RetryInterval = new IntMatch(sp.SecondaryTransport.RetryInterval);
                            sendPort.SecondaryTransport.ServiceWindowEnabled = new BoolMatch(sp.SecondaryTransport.ServiceWindowEnabled);
                            sendPort.SecondaryTransport.FromTime = new DateTimeMatch(sp.SecondaryTransport.FromTime);
                            sendPort.SecondaryTransport.ToTime = new DateTimeMatch(sp.SecondaryTransport.ToTime);

                            if (sp.SecondaryTransport.SendHandler != null)
                            {
                                sendPort.SecondaryTransport.SendHandler = new StringMatch(sp.SecondaryTransport.SendHandler.Name);
                                sendPort.SecondaryTransport.SendHandlerHost = new StringMatch(sp.SecondaryTransport.SendHandler.Host.Name);
                            }
                            if (sp.SecondaryTransport.TransportType != null)
                            {
                                sendPort.SecondaryTransport.TransportType = new StringMatch(sp.SecondaryTransport.TransportType.Name);
                            }
                        }
                    }                    
                    application.SendPorts.Add(sendPort);
                }  
                #endregion
                #endregion
            }
            catch (Exception ex)
            {
                LoadingErrors += ex.ToString() + "\r\n----------------\r\n";
            }
            return application;
        }

        private string ReadReceivePipelineName(btom.Pipeline p)
        {
            if (p == null)
                return "";
            else
                return p.FullName;
        }

        private string N(object val)
        {
            if (val != null)
                return val.ToString();
            else
                return "";
        } 
        #endregion

        #region Comparing groups
        public void CompareGroups(BizTalkGroup group2)
        {
            ComparingErrors = "";
            CompareApplications(Applications, group2.Applications);
        }

        private void CompareApplications(List<Application> applications1, List<Application> applications2)
        {
            bool foundApp = false;
            foreach(Application application1 in applications1)
            {
                try
                {
                    foundApp = false;
                    foreach (Application application2 in applications2)
                    {
                        if (application1.Name == application2.Name)
                        {
                            application1.Match = true;
                            foundApp = true;
                            CompareReferences(application1.References, application2.References);
                            CompareAssemblies(application1.Assemblies, application2.Assemblies);
                            CompareReceivePorts(application1.ReceivePorts, application2.ReceivePorts);
                            CompareSendPorts(application1.SendPorts, application2.SendPorts);
                            break;
                        }
                    }
                    if (!foundApp)
                    {
                        application1.Match = false;
                    }
                }
                catch (Exception ex)
                {
                    ComparingErrors += "Error in application " + application1.Name + "\r\n" + ex.ToString() + "\r\n----------------\r\n";
                }
            }
        }

        private void CompareReferences(List<StringMatch> references1, List<StringMatch> references2)
        {
            bool foundSchema = false;
            if (references1.Count > 0)
            {
                foreach (StringMatch reference1 in references1)
                {
                    foundSchema = false;
                    foreach (StringMatch reference2 in references2)
                    {
                        if (reference1.Name == reference2.Name)
                        {
                            reference1.Match = true;
                            foundSchema = true;
                            break;
                        }
                    }
                    if (!foundSchema)
                    {
                        reference1.Match = false;
                    }
                }
            }
        }
        
        private void CompareReceivePorts(List<ReceivePort> receivePorts1, List<ReceivePort> receivePorts2)
        {
            bool foundReceivePort = false;
            if (receivePorts1.Count > 0)
            {
                foreach(ReceivePort receivePort1 in receivePorts1)
                {
                    foundReceivePort = false;
                    foreach (ReceivePort receivePort2 in receivePorts2)
                    {
                        if (receivePort1.Name == receivePort2.Name)
                        {
                            receivePort1.Match = true;
                            foundReceivePort = true;
                            receivePort1.MatchPropertiesWith(receivePort2);

                            CompareMaps(receivePort1.InboundMaps, receivePort2.InboundMaps);
                            if (receivePort1.IsTwoWay.Value && receivePort1.IsTwoWay.Match)
                                CompareMaps(receivePort1.OutboundMaps, receivePort2.OutboundMaps);
                            CompareReceiveLocations(receivePort1.Receivelocations, receivePort2.Receivelocations);
                            break;
                        }
                    }
                    if (!foundReceivePort)
                    {
                        receivePort1.Match = false;
                    }
                }
            }
        }

        private void CompareReceiveLocations(List<ReceiveLocation> receiveLocations1, List<ReceiveLocation> receiveLocations2)
        {
            bool foundReceiveLocation = false;
            if (receiveLocations1.Count > 0)
            {
                foreach (ReceiveLocation receiveLocation1 in receiveLocations1)
                {
                    foundReceiveLocation = false;
                    foreach (ReceiveLocation receiveLocation2 in receiveLocations2)
                    {
                        if (receiveLocation1.Name == receiveLocation2.Name)
                        {
                            receiveLocation1.Match = true;
                            foundReceiveLocation = true;
                            receiveLocation1.MatchPropertiesWith(receiveLocation2);

                            break;
                        }
                    }
                    if (!foundReceiveLocation)
                    {
                        receiveLocation1.Match = false;
                    }
                }
            }
        }

        private void CompareSendPorts(List<SendPort> sendPorts1, List<SendPort> sendPorts2)
        {
            bool foundSendPort = false;
            if (sendPorts1.Count > 0)
            {
                foreach (SendPort sendPort1 in sendPorts1)
                {
                    foundSendPort = false;
                    foreach (SendPort sendPort2 in sendPorts2)
                    {
                        if (sendPort1.Name == sendPort2.Name)
                        {
                            sendPort1.Match = true;
                            foundSendPort = true;
                            sendPort1.MatchPropertiesWith(sendPort2);
                            //if (sendPort1.PrimaryTransport.IsUsed.Value || sendPort2.PrimaryTransport.IsUsed.Value)
                            {
                                sendPort1.PrimaryTransport.MatchPropertiesWith(sendPort2.PrimaryTransport);
                            }
                            //if (sendPort1.SecondaryTransport.IsUsed.Value || sendPort2.SecondaryTransport.IsUsed.Value)
                            {                                
                                sendPort1.SecondaryTransport.MatchPropertiesWith(sendPort2.SecondaryTransport);
                            }
                            
                            if (sendPort1.IsTwoWay.Value && sendPort1.IsTwoWay.Match)
                                CompareMaps(sendPort1.InboundMaps, sendPort2.InboundMaps);
                            CompareMaps(sendPort1.OutboundMaps, sendPort2.OutboundMaps);

                            break;
                        }
                    }
                    if (!foundSendPort)
                    {
                        sendPort1.Match = false;
                    }
                }
            }
        }

        private void CompareAssemblies(List<HitAssembly> assemblies1, List<HitAssembly> assemblies2)
        {
            bool foundAssembly = false;
            if (assemblies1.Count > 0)
            {
                foreach(HitAssembly hitAssembly1 in assemblies1)
                {
                    foundAssembly = false;
                    foreach (HitAssembly hitAssembly2 in assemblies2)
                    {
                        if (hitAssembly1.DisplayName == hitAssembly2.DisplayName)
                        {
                            hitAssembly1.Match = true;
                            hitAssembly1.Version.Name2 = hitAssembly1.Version.Name;
                            hitAssembly1.Culture.Name2 = hitAssembly1.Culture.Name;
                            foundAssembly = true;
                            CompareSchemas(hitAssembly1.Schemas, hitAssembly2.Schemas);
                            CompareMaps(hitAssembly1.Maps, hitAssembly2.Maps);
                            ComparePipelines(hitAssembly1.Pipelines, hitAssembly2.Pipelines);
                            CompareOrchestrations(hitAssembly1.Orchestrations, hitAssembly2.Orchestrations);
                            break;
                        }
                    }
                    if (!foundAssembly)
                    {
                        hitAssembly1.Match = false;
                        foreach (HitAssembly hitAssembly2 in assemblies2)
                        {
                            if (hitAssembly1.Name == hitAssembly2.Name) // version or culture must be different
                            {
                                hitAssembly1.Match = true;
                                foundAssembly = true;
                                hitAssembly1.Version.Name2 = hitAssembly2.Version.Name;
                                hitAssembly1.Culture.Name2 = hitAssembly2.Culture.Name;   
                                break;
                            }
                        }                        
                    }
                }
            }
        }

        private void CompareSchemas(List<Schema> schemas1, List<Schema> schemas2)
        {
            bool foundSchema = false;
            if (schemas1.Count > 0)
            {
                foreach(Schema schema1 in schemas1)
                {
                    foundSchema = false;
                    foreach (Schema schema2 in schemas2)
                    {
                        if ((schema1.Name == schema2.Name) && //there could be more than one schema with the same fullname
                            (schema1.MessageType == schema2.MessageType))
                        {
                            schema1.Match = true;
                            foundSchema = true;
                            break;
                        }
                    }
                    if (!foundSchema)
                    {
                        schema1.Match = false;
                    }
                }
            }
        }

        private void CompareMaps(List<StringMatch> maps1, List<StringMatch> maps2)
        {
            bool foundMap = false;
            if (maps1.Count > 0)
            {
                foreach(StringMatch map1 in maps1)
                {
                    foundMap = false;
                    foreach (StringMatch map2 in maps2)
                    {
                        if (map1.Name == map2.Name)
                        {
                            map1.Match = true;
                            foundMap = true;
                            break;
                        }
                    }
                    if (!foundMap)
                    {
                        map1.Match = false;
                    }
                }
            }
        }

        private void ComparePipelines(List<StringMatch> pipelines1, List<StringMatch> pipelines2)
        {
            bool foundPipeline = false;
            if (pipelines1.Count > 0)
            {
                foreach(StringMatch pipeline1 in pipelines1)
                {
                    foundPipeline = false;
                    foreach (StringMatch pipeline2 in pipelines2)
                    {
                        if (pipeline1.Name == pipeline2.Name)
                        {
                            pipeline1.Match = true;
                            foundPipeline = true;
                            break;
                        }
                    }
                    if (!foundPipeline)
                    {
                        pipeline1.Match = false;
                    }
                }
            }
        }

        private void CompareOrchestrations(List<Orchestration> orchestrations1, List<Orchestration> orchestrations2)
        {
            bool foundOrchestration = false;
            if (orchestrations1.Count > 0)
            {
                foreach(Orchestration orchestration1 in orchestrations1)
                {
                    foundOrchestration = false;
                    foreach (Orchestration orchestration2 in orchestrations2)
                    {
                        if (orchestration1.Name == orchestration2.Name)
                        {
                            orchestration1.Match = true;
                            orchestration1.Host.Name2 = orchestration2.Host.Name;

                            foundOrchestration = true;
                            CompareOrchestrationPorts(orchestration1.Ports, orchestration2.Ports);
                            break;
                        }
                    }
                    if (!foundOrchestration)
                    {
                        orchestration1.Match = false;
                    }
                }
            }
        }

        private void CompareOrchestrationPorts(List<StringMatch> ports1, List<StringMatch> ports2)
        {
            bool foundPipeline = false;
            if (ports1.Count > 0)
            {
                foreach(StringMatch port1 in ports1)
                {
                    foundPipeline = false;
                    foreach (StringMatch port2 in ports2)
                    {
                        if (port1.Name == port2.Name)
                        {
                            port1.Match = true;
                            foundPipeline = true;
                            break;
                        }
                    }
                    if (!foundPipeline)
                    {
                        port1.Match = false;
                    }
                }
            }
        }

        #endregion
    }
}
